package com.mercury.ec.framework.air.core.client.impl.skyecho.util;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.Security;

public class AESPKCS7PaddingUtil {
    /**
     * 编码
     */
    public static String CHARSET = "UTF-8";
    public static String ALGORITHM = "AES";
    /**
     * 加密方式：ECB、CBC、CFB、OFB、CTR
     * 填充方式：PKCS5Padding、PKCS7Padding、zeropadding、iso10126、ansix923
     * 数据块长度：128位、192位、256位
     */
    public static String ALGORITHM_ECB_5 = "AES/ECB/PKCS5Padding";
    public static String ALGORITHM_ECB_7 = "AES/ECB/PKCS7Padding";
    public static String ALGORITHM_ECB_z = "AES/ECB/zeroPadding";
    public static String ALGORITHM_ECB_i = "AES/ECB/iso10126";
    public static String ALGORITHM_ECB_a = "AES/ECB/ansix923";
    public static String ALGORITHM_CBC_5 = "AES/CBC/PKCS5Padding";
    public static String ALGORITHM_CBC_7 = "AES/CBC/PKCS7Padding";
    public static String ALGORITHM_CBC_z = "AES/CBC/zeroPadding";
    public static String ALGORITHM_CBC_i = "AES/CBC/iso10126";
    public static String ALGORITHM_CBC_a = "AES/CBC/ansix923";

    /**
     * key的长度
     * 16=128 bit
     * 24=192 bit
     * 32=256 bit
     */
    private static int ALGORITHM_128 = 16;
    private static int ALGORITHM_192 = 24;
    private static int ALGORITHM_256 = 32;

    static {
        String errorString = "Failed manually overriding key-length permissions.";
        int newMaxKeyLength;
        try {
            if ((newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES")) < 256) {
                Class c = Class.forName("javax.crypto.CryptoAllPermissionCollection");
                Constructor con = c.getDeclaredConstructor();
                con.setAccessible(true);
                Object allPermissionCollection = con.newInstance();
                Field f = c.getDeclaredField("all_allowed");
                f.setAccessible(true);
                f.setBoolean(allPermissionCollection, true);
                c = Class.forName("javax.crypto.CryptoPermissions");
                con = c.getDeclaredConstructor();
                con.setAccessible(true);
                Object allPermissions = con.newInstance();
                f = c.getDeclaredField("perms");
                f.setAccessible(true);
                ((java.util.Map) f.get(allPermissions)).put("*", allPermissionCollection);
                c = Class.forName("javax.crypto.JceSecurityManager");
                f = c.getDeclaredField("defaultPolicy");
                f.setAccessible(true);
                Field mf = Field.class.getDeclaredField("modifiers");
                mf.setAccessible(true);
                mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
                f.set(null, allPermissions);
                newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
            }
        } catch (Exception e) {
            throw new RuntimeException(errorString, e);
        }
        if (newMaxKeyLength < 256)
            throw new RuntimeException(errorString); // hack failed
    }

    /**
     * encode ecb
     *
     * @param key     密钥
     * @param content 明文
     * @param key     加密方式/补码方式
     * @return
     */
    public static String encodeECB(String content, String key) {
        try {
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_7);
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET), ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            byte[] bytes = cipher.doFinal(content.getBytes("GB18030"));
            return Base64.encode(bytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String decodeECB(String content, String key) {
        try {
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_7);
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET), ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] bytes = cipher.doFinal(Base64.decode(content));
            return new String(bytes, "GB18030");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * encode cbc
     *
     * @param key           密钥
     * @param content       明文
     * @param algorithmEnum 加密方式/补码方式
     * @param iv            使用CBC模式，需要一个向量iv，可增加加密算法的强度
     * @return
     */
    public static String encodeCBC(String key, String content, String iv) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_CBC_5);
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET), ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(CHARSET));
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
            byte[] bytes = cipher.doFinal(content.getBytes(CHARSET));
            return new BASE64Encoder().encode(bytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
